﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Text.RegularExpressions;

namespace Ex10.FileEncryption
{
    class FileEncryption
    {

        private const string INPUT_FILE = "..\\..\\file.mp3";
        private const string OUTPUT_FILE = "..\\..\\file.out";

        private const int RIJNDAEL_SECRET_KEY_SIZE_BITS = 256;
        private const int RIJNDAEL_INITIAL_VECTOR_SIZE_BITS = 128;
        private const int BUFFER_SIZE = 1024 * 1024;

        static void Main()
        {
            string password = "$3cR3t-P@$$w0rd";
            
            //SplitAndEncryptFile(password);

            //MergeAndDecrypt(password);

        }

        private static void MergeAndDecrypt(string password)
        {
            string extension = Path.GetExtension(INPUT_FILE);
            int lastIndex = INPUT_FILE.LastIndexOf('\\');
            string directory = INPUT_FILE.Substring(0, INPUT_FILE.Length - lastIndex - extension.Length + 1);
            string[] files = Directory.GetFiles(directory, string.Format("*{0}", extension));

            string expression = string.Format(@"\w*-part\d*");
            string filePathWithoutExtension = INPUT_FILE.Substring(0, INPUT_FILE.Length - extension.Length);

            string outputFileName = string.Format("{0}-original{1}", filePathWithoutExtension, extension);
            File.Create(outputFileName).Dispose();

            foreach (var file in files)
            {
                bool isPart = Regex.IsMatch(file, expression);
                if (isPart)
                {
                    using (FileStream inFile = new FileStream(file, FileMode.Open, FileAccess.Read))
                    {
                        byte[] buf = new byte[BUFFER_SIZE];
                        while (true)
                        {
                            int bytesRead = inFile.Read(buf, 0, buf.Length);
                            if (bytesRead == 0)
                            {
                                break;
                            }
                            using (FileStream outFile = new FileStream(outputFileName, FileMode.Append))
                            {
                                //byte[] decryptedBytes = DecryptBytes(buf, password);
                                byte[] decryptedBytes = buf;
                                outFile.Write(decryptedBytes, 0, decryptedBytes.Length);
                            }
                        }
                    }
                }
            }
        }

        private static void SplitAndEncryptFile(string password)
        {
            using (FileStream inFile = new FileStream(INPUT_FILE, FileMode.Open, FileAccess.Read))
            {
                byte[] buf = new byte[BUFFER_SIZE];

                string extension = Path.GetExtension(INPUT_FILE);
                int extensionLength = extension.Length;
                string filePathWithoutExtension = INPUT_FILE.Substring(0, INPUT_FILE.Length - extension.Length);
                int partsCounter = 0;

                while (true)
                {
                    int bytesRead = inFile.Read(buf, 0, buf.Length);
                    if (bytesRead == 0) break;
                    string outputFileName = string.Format("{0}-part{1:000}{2}",
                        filePathWithoutExtension, partsCounter, extension);
                    using (FileStream outFile = new FileStream(outputFileName, FileMode.Create))
                    {
                        //byte[] encryptedBytes = EncryptBytes(buf, password);
                        byte[] encryptedBytes = buf;
                        outFile.Write(encryptedBytes, 0, bytesRead);
                        partsCounter++;
                    }
                }
            }
        }

        private static ICryptoTransform CreateRijndaelEncryptor(string password)
        {
            byte[] salt = Encoding.ASCII.GetBytes(password.Length.ToString());

            PasswordDeriveBytes passwordDerivedBytes = new PasswordDeriveBytes(password, salt);

            byte[] secretKey = passwordDerivedBytes.GetBytes(RIJNDAEL_SECRET_KEY_SIZE_BITS / 8);
            byte[] initialVector = passwordDerivedBytes.GetBytes(RIJNDAEL_INITIAL_VECTOR_SIZE_BITS / 8);

            RijndaelManaged RijndaelCipher = new RijndaelManaged();
            ICryptoTransform encryptor = RijndaelCipher.CreateEncryptor(secretKey, initialVector);

            return encryptor;
        }
        private static ICryptoTransform CreateRijndaelDecryptor(string password)
        {
            byte[] salt = Encoding.ASCII.GetBytes(password.Length.ToString());
            PasswordDeriveBytes passwordDerivedBytes = new PasswordDeriveBytes(password, salt);
            byte[] secretKey = passwordDerivedBytes.GetBytes(RIJNDAEL_SECRET_KEY_SIZE_BITS / 8);
            byte[] initialVector = passwordDerivedBytes.GetBytes(RIJNDAEL_INITIAL_VECTOR_SIZE_BITS / 8);
            RijndaelManaged RijndaelCipher = new RijndaelManaged();
            ICryptoTransform decryptor = RijndaelCipher.CreateDecryptor(secretKey, initialVector);
            return decryptor;
        }

        private static byte[] EncryptBytes(byte[] bytesToEncrypt, string password)
        {
            ICryptoTransform encryptor = CreateRijndaelEncryptor(password);
            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream =
                new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
            using (cryptoStream)
            {
                cryptoStream.Write(bytesToEncrypt, 0, bytesToEncrypt.Length);
                cryptoStream.FlushFinalBlock();
                byte[] encryptedBytes = memoryStream.ToArray();
                return encryptedBytes;
            }
        }
        private static byte[] DecryptBytes(byte[] encryptedBytes, string password)
        {
            ICryptoTransform encryptor = CreateRijndaelDecryptor(password);
            MemoryStream encryptedBytesStream = new MemoryStream(encryptedBytes);
            MemoryStream decryptedBytesStream = new MemoryStream();
            CryptoStream cryptoStream =
                new CryptoStream(encryptedBytesStream, encryptor, CryptoStreamMode.Read);
            using (cryptoStream)
            {
                byte[] buffer = new byte[encryptedBytes.Length];
                while (true)
                {
                    int bytesRead = cryptoStream.Read(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                    {
                        break;
                    }
                    decryptedBytesStream.Write(buffer, 0, bytesRead);
                }
                byte[] decryptedBytes = decryptedBytesStream.ToArray();
                return decryptedBytes;
            }
        }
    }
}
